// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
//==========================================================================
//  File:       HttpChannelHelper.cs 
// 
//  Summary:    Implements helper methods for http client and server channels.
// 
//=========================================================================

using System;
using System.Net; 
using System.Net.Sockets;
using System.Text; 
using System.Globalization; 

namespace System.Runtime.Remoting.Channels.Http 
{


    internal static class HttpChannelHelper 
    {
        private const String _http = "http://"; 
#if !FEATURE_PAL 
        private const String _https = "https://";
#endif 

        private static char[] s_semicolonSeparator = new char[]{';'};

 
        // Determine if the url starts with "http://"
#if !FEATURE_PAL 
        // or "https://" 
#endif
        internal static int StartsWithHttp(String url) 
        {
            int urlLength = url.Length;

            if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _http)) 
                return _http.Length;
#if !FEATURE_PAL 
            else 
            if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _https))
                return _https.Length; 
#endif
            else
                return -1;
        } // StartsWithHttp 

 
        // Used by http channels to implement IChannel::Parse. 
        // It returns the channel uri and places object uri into out parameter.
        internal static String ParseURL(String url, out String objectURI) 
        {
            // Set the out parameters
            objectURI = null;
 
            int separator = StartsWithHttp(url);
            if (separator == -1) 
                return null; 

            // find next slash (after end of scheme) 
            separator = url.IndexOf('/', separator);
            if (-1 == separator)
            {
                return url;  // means that the url is just "tcp://foo:90" or something like that 
            }
 
            // Extract the channel URI which is the prefix 
            String channelURI = url.Substring(0, separator);
 
            // Extract the object URI which is the suffix (leave the slash)
            objectURI = url.Substring(separator);

            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse URI in: " + url); 
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse channelURI: " + channelURI);
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse objectURI: " + objectURI); 
 
            return channelURI;
        } // ParseURL 


        internal static String GetObjectUriFromRequestUri(String uri)
        { 
            // We assume uri may be in one of the following forms
            //   http://myhost.com/myobject.rem 
#if !FEATURE_PAL 
            //   https://myhost.com/myobject.rem
#endif 
            //   /myobject.rem
            //   /myobject
            //   myobject.rem
            // In all cases, myobject is considered to be the object URI (.rem might be absent) 

            int start, end; // range of characters to use 
            int index; 
            start = 0;
            end = uri.Length; 

            // first see if uri starts with http://
#if !FEATURE_PAL
            // or https:// 
#endif
            // and remove up to next slash if it does 
            start = StartsWithHttp(uri); 
            if (start != -1)
            { 
                // remove domain name as well
                index = uri.IndexOf('/', start);
                if (index != -1)
                    start = index + 1; 
                else
                    start = end; // uri will end up being "" 
            } 
            else
            { 
                // remove "/" if this is an absolute path
                start = 0;
                if (uri[start] == '/')
                   start++; 
            }
 
            // remove query string if present ('?' and everything past it) 
            index = uri.IndexOf('?');
            if (index != -1) 
                end = index;

            if (start < end)
                return CoreChannel.RemoveApplicationNameFromUri(uri.Substring(start, end - start)); 
            else
                return ""; 
        } // GetObjectURIFromRequestURI 

 
        internal static void ParseContentType(String contentType,
                                              out String value,
                                              out String charset)
        { 
            charset = null;
 
            if (contentType == null) 
            {
                value = null; 
                return;
            }

            String[] parts = contentType.Split(s_semicolonSeparator); 

            // the actual content-type value is always first 
            value = parts[0]; 

            // examine name value pairs and look for charset 
            if (parts.Length > 0)
            {
                foreach (String part in parts)
                { 
                    int index = part.IndexOf('=');
                    if (index != -1) 
                    { 
                        String key = part.Substring(0, index).Trim();
                        if (String.Compare(key, "charset", StringComparison.OrdinalIgnoreCase) == 0) 
                        {
                            if ((index + 1) < part.Length)
                            {
                                // we had to make sure there is something after the 
                                //   equals sign.
                                charset = part.Substring(index + 1); 
                            } 
                            else
                            { 
                                charset = null;
                            }
                            return;
                        } 
                    }
                } // foreach 
            } 
        } // ParseContentType
 
        internal static String ReplaceChannelUriWithThisString(String url, String channelUri)
        {
            // NOTE: channelUri is assumed to be scheme://machinename:port
            //   with NO trailing slash. 

            String oldChannelUri; 
            String objUri; 
            oldChannelUri = HttpChannelHelper.ParseURL(url, out objUri);
            InternalRemotingServices.RemotingAssert(oldChannelUri != null, "http url expected."); 
            InternalRemotingServices.RemotingAssert(objUri != null, "non-null objUri expected.");

            return channelUri + objUri;
        } // ReplaceChannelUriWithThisString 

 
        // returns url with the machine name replaced with the ip address. 
        internal static String ReplaceMachineNameWithThisString(String url, String newMachineName)
        { 
            String objectUri;
            String channelUri = ParseURL(url, out objectUri);

            // find bounds of machine name 
            int index = StartsWithHttp(url);
            if (index == -1) 
                return url; 

            int colonIndex = channelUri.IndexOf(':', index); 
            if (colonIndex == -1)
                colonIndex = channelUri.Length;

            // machine name is between index and up to but not including colonIndex, 
            //   so we will replace those characters with the ip address.
            String newUrl = url.Substring(0, index) + newMachineName + url.Substring(colonIndex); 
            return newUrl; 
        } // ReplaceMachineNameWithIpAddress
 

        // Decodes a uri while it is in byte array form
        internal static void DecodeUriInPlace(byte[] uriBytes, out int length)
        { 
            int percentsFound = 0;
            int count = uriBytes.Length; 
            length = count; 
            int co = 0;
            while (co < count) 
            {
                if (uriBytes[co] == (byte)'%')
                {
                    // determine location to write to (we skip 2 character for each percent) 
                    int writePos = co - (percentsFound * 2);
 
                    // decode in place by collapsing bytes "%XY" (actual byte is 16*Dec(X) + Dec(Y)) 
                    uriBytes[writePos] = (byte)
                        (16 * CharacterHexDigitToDecimal(uriBytes[co + 1]) + 
                         CharacterHexDigitToDecimal(uriBytes[co + 2]));

                    percentsFound++;
                    length -= 2; // we eliminated 2 characters from the length 
                    co += 3;
                } 
                else 
                {
                    if (percentsFound != 0) 
                    {
                        // we have to copy characters back into place since we will skip some characters

                        // determine location to write to (we skip 2 character for each percent) 
                        int writePos = co - (percentsFound * 2);
 
                        // copy character back into place 
                        uriBytes[writePos] = uriBytes[co];
                    } 

                    co++;
                }
            } 

        } // DecodeUri 
 

 
        // reading helper functions
        internal static int CharacterHexDigitToDecimal(byte b)
        {
            switch ((char)b) 
            {
            case 'F': return 15; 
            case 'E': return 14; 
            case 'D': return 13;
            case 'C': return 12; 
            case 'B': return 11;
            case 'A': return 10;
            default: return b - (byte)'0';
            } 
        } // CharacterHexDigitToDecimal
 
 
        internal static char DecimalToCharacterHexDigit(int i)
        { 
            switch (i)
            {
            case 15: return 'F';
            case 14: return 'E'; 
            case 13: return 'D';
            case 12: return 'C'; 
            case 11: return 'B'; 
            case 10: return 'A';
            default: return (char)(i + (byte)'0'); 
            }

        } // DecimalToCharacterHexDigit
 

 
    } // class HttpChannelHelper 

 

    internal static class HttpEncodingHelper
    {
        internal static String EncodeUriAsXLinkHref(String uri) 
        {
            if (uri == null) 
                return null; 

            // uses modified encoding rules from xlink href spec for encoding uri's. 
            // http://www.w3.org/TR/2000/PR-xlink-20001220/#link-locators

            byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
 
            StringBuilder sb = new StringBuilder(uri.Length);
 
            // iterate over uri bytes and build up an encoded string. 
            foreach (byte b in uriBytes)
            { 
                if (!EscapeInXLinkHref(b))
                {
                    sb.Append((char)b);
                } 
                else
                { 
                    // the character needs to be encoded as %HH 
                    sb.Append('%');
                    sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b >> 4)); 
                    sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b & 0xF));
                }
            }
 
            return sb.ToString();
        } // EncodeUriAsXLinkHref 
 

        internal static bool EscapeInXLinkHref(byte ch) 
        {
            if ((ch <= 32) || // control characters and space
                (ch >= 128) ||  // non-ascii characters
                (ch == (byte)'<') || 
                (ch == (byte)'>') ||
                (ch == (byte)'"')) 
            { 
                return true;
            } 

            return false;
        } // EscapeInXLinkHref
 

        internal static String DecodeUri(String uri) 
        { 
            byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
 
            int length;
            HttpChannelHelper.DecodeUriInPlace(uriBytes, out length);

            String newUri = Encoding.UTF8.GetString(uriBytes, 0, length); 
            return newUri;
        } // DecodeUri 
 
    } // class HttpEncodingHelper
 


} // namespace System.Runtime.Remoting.Channels.Http

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
// ==++== 
//
//   Copyright (c) Microsoft Corporation.  All rights reserved.
//
// ==--== 
//==========================================================================
//  File:       HttpChannelHelper.cs 
// 
//  Summary:    Implements helper methods for http client and server channels.
// 
//=========================================================================

using System;
using System.Net; 
using System.Net.Sockets;
using System.Text; 
using System.Globalization; 

namespace System.Runtime.Remoting.Channels.Http 
{


    internal static class HttpChannelHelper 
    {
        private const String _http = "http://"; 
#if !FEATURE_PAL 
        private const String _https = "https://";
#endif 

        private static char[] s_semicolonSeparator = new char[]{';'};

 
        // Determine if the url starts with "http://"
#if !FEATURE_PAL 
        // or "https://" 
#endif
        internal static int StartsWithHttp(String url) 
        {
            int urlLength = url.Length;

            if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _http)) 
                return _http.Length;
#if !FEATURE_PAL 
            else 
            if (StringHelper.StartsWithAsciiIgnoreCasePrefixLower(url, _https))
                return _https.Length; 
#endif
            else
                return -1;
        } // StartsWithHttp 

 
        // Used by http channels to implement IChannel::Parse. 
        // It returns the channel uri and places object uri into out parameter.
        internal static String ParseURL(String url, out String objectURI) 
        {
            // Set the out parameters
            objectURI = null;
 
            int separator = StartsWithHttp(url);
            if (separator == -1) 
                return null; 

            // find next slash (after end of scheme) 
            separator = url.IndexOf('/', separator);
            if (-1 == separator)
            {
                return url;  // means that the url is just "tcp://foo:90" or something like that 
            }
 
            // Extract the channel URI which is the prefix 
            String channelURI = url.Substring(0, separator);
 
            // Extract the object URI which is the suffix (leave the slash)
            objectURI = url.Substring(separator);

            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse URI in: " + url); 
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse channelURI: " + channelURI);
            InternalRemotingServices.RemotingTrace("HTTPChannel.Parse objectURI: " + objectURI); 
 
            return channelURI;
        } // ParseURL 


        internal static String GetObjectUriFromRequestUri(String uri)
        { 
            // We assume uri may be in one of the following forms
            //   http://myhost.com/myobject.rem 
#if !FEATURE_PAL 
            //   https://myhost.com/myobject.rem
#endif 
            //   /myobject.rem
            //   /myobject
            //   myobject.rem
            // In all cases, myobject is considered to be the object URI (.rem might be absent) 

            int start, end; // range of characters to use 
            int index; 
            start = 0;
            end = uri.Length; 

            // first see if uri starts with http://
#if !FEATURE_PAL
            // or https:// 
#endif
            // and remove up to next slash if it does 
            start = StartsWithHttp(uri); 
            if (start != -1)
            { 
                // remove domain name as well
                index = uri.IndexOf('/', start);
                if (index != -1)
                    start = index + 1; 
                else
                    start = end; // uri will end up being "" 
            } 
            else
            { 
                // remove "/" if this is an absolute path
                start = 0;
                if (uri[start] == '/')
                   start++; 
            }
 
            // remove query string if present ('?' and everything past it) 
            index = uri.IndexOf('?');
            if (index != -1) 
                end = index;

            if (start < end)
                return CoreChannel.RemoveApplicationNameFromUri(uri.Substring(start, end - start)); 
            else
                return ""; 
        } // GetObjectURIFromRequestURI 

 
        internal static void ParseContentType(String contentType,
                                              out String value,
                                              out String charset)
        { 
            charset = null;
 
            if (contentType == null) 
            {
                value = null; 
                return;
            }

            String[] parts = contentType.Split(s_semicolonSeparator); 

            // the actual content-type value is always first 
            value = parts[0]; 

            // examine name value pairs and look for charset 
            if (parts.Length > 0)
            {
                foreach (String part in parts)
                { 
                    int index = part.IndexOf('=');
                    if (index != -1) 
                    { 
                        String key = part.Substring(0, index).Trim();
                        if (String.Compare(key, "charset", StringComparison.OrdinalIgnoreCase) == 0) 
                        {
                            if ((index + 1) < part.Length)
                            {
                                // we had to make sure there is something after the 
                                //   equals sign.
                                charset = part.Substring(index + 1); 
                            } 
                            else
                            { 
                                charset = null;
                            }
                            return;
                        } 
                    }
                } // foreach 
            } 
        } // ParseContentType
 
        internal static String ReplaceChannelUriWithThisString(String url, String channelUri)
        {
            // NOTE: channelUri is assumed to be scheme://machinename:port
            //   with NO trailing slash. 

            String oldChannelUri; 
            String objUri; 
            oldChannelUri = HttpChannelHelper.ParseURL(url, out objUri);
            InternalRemotingServices.RemotingAssert(oldChannelUri != null, "http url expected."); 
            InternalRemotingServices.RemotingAssert(objUri != null, "non-null objUri expected.");

            return channelUri + objUri;
        } // ReplaceChannelUriWithThisString 

 
        // returns url with the machine name replaced with the ip address. 
        internal static String ReplaceMachineNameWithThisString(String url, String newMachineName)
        { 
            String objectUri;
            String channelUri = ParseURL(url, out objectUri);

            // find bounds of machine name 
            int index = StartsWithHttp(url);
            if (index == -1) 
                return url; 

            int colonIndex = channelUri.IndexOf(':', index); 
            if (colonIndex == -1)
                colonIndex = channelUri.Length;

            // machine name is between index and up to but not including colonIndex, 
            //   so we will replace those characters with the ip address.
            String newUrl = url.Substring(0, index) + newMachineName + url.Substring(colonIndex); 
            return newUrl; 
        } // ReplaceMachineNameWithIpAddress
 

        // Decodes a uri while it is in byte array form
        internal static void DecodeUriInPlace(byte[] uriBytes, out int length)
        { 
            int percentsFound = 0;
            int count = uriBytes.Length; 
            length = count; 
            int co = 0;
            while (co < count) 
            {
                if (uriBytes[co] == (byte)'%')
                {
                    // determine location to write to (we skip 2 character for each percent) 
                    int writePos = co - (percentsFound * 2);
 
                    // decode in place by collapsing bytes "%XY" (actual byte is 16*Dec(X) + Dec(Y)) 
                    uriBytes[writePos] = (byte)
                        (16 * CharacterHexDigitToDecimal(uriBytes[co + 1]) + 
                         CharacterHexDigitToDecimal(uriBytes[co + 2]));

                    percentsFound++;
                    length -= 2; // we eliminated 2 characters from the length 
                    co += 3;
                } 
                else 
                {
                    if (percentsFound != 0) 
                    {
                        // we have to copy characters back into place since we will skip some characters

                        // determine location to write to (we skip 2 character for each percent) 
                        int writePos = co - (percentsFound * 2);
 
                        // copy character back into place 
                        uriBytes[writePos] = uriBytes[co];
                    } 

                    co++;
                }
            } 

        } // DecodeUri 
 

 
        // reading helper functions
        internal static int CharacterHexDigitToDecimal(byte b)
        {
            switch ((char)b) 
            {
            case 'F': return 15; 
            case 'E': return 14; 
            case 'D': return 13;
            case 'C': return 12; 
            case 'B': return 11;
            case 'A': return 10;
            default: return b - (byte)'0';
            } 
        } // CharacterHexDigitToDecimal
 
 
        internal static char DecimalToCharacterHexDigit(int i)
        { 
            switch (i)
            {
            case 15: return 'F';
            case 14: return 'E'; 
            case 13: return 'D';
            case 12: return 'C'; 
            case 11: return 'B'; 
            case 10: return 'A';
            default: return (char)(i + (byte)'0'); 
            }

        } // DecimalToCharacterHexDigit
 

 
    } // class HttpChannelHelper 

 

    internal static class HttpEncodingHelper
    {
        internal static String EncodeUriAsXLinkHref(String uri) 
        {
            if (uri == null) 
                return null; 

            // uses modified encoding rules from xlink href spec for encoding uri's. 
            // http://www.w3.org/TR/2000/PR-xlink-20001220/#link-locators

            byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
 
            StringBuilder sb = new StringBuilder(uri.Length);
 
            // iterate over uri bytes and build up an encoded string. 
            foreach (byte b in uriBytes)
            { 
                if (!EscapeInXLinkHref(b))
                {
                    sb.Append((char)b);
                } 
                else
                { 
                    // the character needs to be encoded as %HH 
                    sb.Append('%');
                    sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b >> 4)); 
                    sb.Append(HttpChannelHelper.DecimalToCharacterHexDigit(b & 0xF));
                }
            }
 
            return sb.ToString();
        } // EncodeUriAsXLinkHref 
 

        internal static bool EscapeInXLinkHref(byte ch) 
        {
            if ((ch <= 32) || // control characters and space
                (ch >= 128) ||  // non-ascii characters
                (ch == (byte)'<') || 
                (ch == (byte)'>') ||
                (ch == (byte)'"')) 
            { 
                return true;
            } 

            return false;
        } // EscapeInXLinkHref
 

        internal static String DecodeUri(String uri) 
        { 
            byte[] uriBytes = Encoding.UTF8.GetBytes(uri);
 
            int length;
            HttpChannelHelper.DecodeUriInPlace(uriBytes, out length);

            String newUri = Encoding.UTF8.GetString(uriBytes, 0, length); 
            return newUri;
        } // DecodeUri 
 
    } // class HttpEncodingHelper
 


} // namespace System.Runtime.Remoting.Channels.Http

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
